#ifndef __VANILA_CHUNK_HH__
#define __VANILA_CHUNK_HH__

#include "vanila/opcode.h"
#include "vanila/value.h"
#include <cstdint>
#include <vector>

namespace vanila
{
//! @brief store the opcode
class Chunk
{
public:
    Chunk();
    ~Chunk() noexcept;

public:
    //! \brief insert a opcode to code's vector
    void writeBytecode(OpCode opcode, int line);

    //! \brief insert a byet to code's vector
    void writeByte(uint8_t byte, int line);

    //! \brief add a constant value to chunk's constants vector
    size_t addConstant(const Value& value);
    
    //! \brief write a CONSTANT byte code 
    void writeConstant(const Value& value, int line);

public:
    //! \brief read the OpCode in specify index of codes vector
    OpCode readBytecode(size_t index) const noexcept
    { return this->_codes[index]; }

    //! \brief read the byte value in specify index of codes vector
    uint8_t readByte(size_t index) const noexcept
    { return static_cast<uint8_t>(this->_codes[index]); }

    //! \brief read the short value in specify index of codes vector
    uint16_t readShort(size_t index) const noexcept
    { return static_cast<uint16_t>( (this->readByte(index) << 8) | this->readByte(index + 1)); }

    //! \brief get the constant value in specify index
    Value& getConstant(size_t index) noexcept
    { return this->_constants[index]; }  

    //! \brief get the constant value in specify index
    const Value& getConstant(size_t index) const noexcept
    { return this->_constants[index]; }

public:
    //! \brief Set the specify bytecode to specify index of code vector
    void setBytecode(size_t index, OpCode bytecode) noexcept
    { this->_codes[index] = bytecode; }

    //! \brief Set the specify byte value to specify index of code vector
    void setByte(size_t index, uint8_t byte) noexcept
    { this->_codes[index] = static_cast<OpCode>(byte); }

private:
    std::vector<OpCode> _codes;
    std::vector<Value> _constants;
    std::vector<int> _lines;

public:
    std::vector<OpCode>& codes() noexcept
    { return this->_codes; }
    
    const std::vector<OpCode>& codes() const noexcept
    { return this->_codes; }
 
    std::vector<Value>& constants() noexcept
    { return this->_constants; }
    
    const std::vector<Value>& constants() const noexcept
    { return this->_constants; }

    std::vector<int>& lines() noexcept
    { return this->_lines; }
    
    const std::vector<int>& lines() const noexcept
    { return this->_lines; }

    size_t size() const noexcept
    { return this->_codes.size(); }

    size_t capacity() const noexcept
    { return this->_codes.capacity(); }

    OpCode* data() noexcept
    { return this->_codes.data(); }
    
    const OpCode* data() const noexcept
    { return this->_codes.data(); }
};

}

#endif